package com.fxu.framework.biz.base;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.fxu.framework.core.base.Constant;
import com.fxu.framework.core.exception.MsgException;
import com.fxu.framework.core.sql.*;
import com.fxu.framework.core.util.FieldUtil;
import com.fxu.framework.core.util.MapUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.fxu.framework.core.base.Constant.QUERY_LINK;

/**
 * vue通用参数类
 * @author fangxu
 * @version 1.0.0
 */
@Getter
public class Param<SE extends SEntity<SE>> {
	private final int page; // 第几页
	private final int size; // 每页大小
	private final Long nextId; // 下一页起始ID
	private SQuery<SE> sQuery = null; // 查询类
	private final boolean noCount; // 不需要查询总数
	private final boolean isIdAsc; // 是否order by id asc
	private final boolean showText; // 是否显示大字段[text,blob]
	private final boolean showDefault; // 是否显示创建者、修改者、修改时间
	private final String showFlags; // 显示字段集合
	private final String orderBy; // 排序，多个逗号[,]隔开[=NULL不排序,id_asc=order by id asc,id_desc=order by id desc]
	private final Map<String, Object> map; // 总的入参map
	private final Map<String, Object> params; // 查询map
	private final List<String> showFlag = new ArrayList<>(); // 显示字段集合
	private final List<SValue> queryList = new ArrayList<>(); // 查询条件集合
	private final List<SValue> andOrList = new ArrayList<>(); // 查询条件集合
	private final List<List<SValue>> orParams = new ArrayList<>(); // Or查询条件集合
	private final List<SField<SE>> showFields = new ArrayList<>(); // 显示字段集合
	private List<SField<?>> showFlagList = new ArrayList<>(); // 显示条件集合
	private Map<SField<SE>, List<SField<?>>> cascadeMap = new HashMap<>(); // 级联条件集合

	/**
	 * 初始化函数
	 * @param clazz 类别
	 * @param map 前端传入查询条件
	 * @param pQuery filterMap处理后查询条件
	 * @param filterEmpty 是否过滤null或者""
	 */
	public Param(Class<SE> clazz, Map<String, Object> map, SQuery<SE> pQuery, boolean filterEmpty) {
		this.map = map == null ? new HashMap<>() : map;
		this.page = MapUtil.getInt(map, Constant.PAGE_NUM, Constant.DEFAULT_PAGE_NUM);
		this.size = MapUtil.getInt(map, Constant.PAGE_SIZE, Constant.DEFAULT_PAGE_SIZE);
		this.nextId = MapUtil.getLong(map, Constant.NEXT_ID, null);
		this.showFlags = MapUtil.getStr(map, Constant.SHOW_FLAGS, "");
		this.showDefault = MapUtil.getBool(map, Constant.SHOW_DEFAULT, true);
		String tempFields = MapUtil.getStr(map, Constant.SHOW_FIELDS, "");
		this.showText = MapUtil.getBool(map, Constant.SHOW_TEXT, false);
		this.isIdAsc = MapUtil.getBool(map, Constant.IS_ID_ASC, false);
		this.noCount = MapUtil.getBool(map, Constant.NO_COUNT, false);
		this.orderBy = MapUtil.getStr(map, Constant.ORDER_BY, Constant.ID_NAME + "$desc");
		if (!this.map.containsKey(Constant.PARAMS)) {
			this.params = new HashMap<>();
		} else {
			@SuppressWarnings("unchecked")
			Map<String, Object> tmps = (Map<String, Object>) this.map.get(Constant.PARAMS);
			this.params = tmps;
		}

		List<String> removes = new ArrayList<>();
		for (String key : params.keySet()) {
			Object value = params.get(key);
			// 过滤掉null和""
			if (filterEmpty && (value == null || StrUtil.isEmpty(value.toString()))) {
				removes.add(key);
			} else if (clazz != null && params.get(key) != null) {
				String newKey = key;
				SType sType = SType.EQ;
				boolean isApply = false;

				// 各种类型查询
				int index = key.indexOf(Constant.AND_OR_LINK);
				if (index > 0) {
					sType = getQueryType(key.substring(index).replace(Constant.AND_OR_LINK, QUERY_LINK));
					if (sType == null) {
						throw new MsgException("参数错误!key=" + key);
					}
					newKey = key.substring(0, index);
					// apply 的情况
					if (key.substring(index).startsWith(Constant.APPLY_LINK)) {
						andOrList.add(new SValue(sType, new SField<>(clazz, newKey),
								new SField<>(clazz, String.valueOf(value))));
					} else {
						if (value instanceof ArrayList) {
							@SuppressWarnings("unchecked")
							ArrayList<Object> values = (ArrayList<Object>) value;
							for (Object sValue : values) {
								andOrList.add(new SValue(sType, new SField<>(clazz, newKey), sValue));
							}
						} else {
							andOrList.add(new SValue(sType, new SField<>(clazz, newKey), value));
						}
					}
				} else {
					index = key.indexOf(QUERY_LINK);
					if (index > 0) {
						sType = getQueryType(key.substring(index));
						if (sType == null) {
							throw new MsgException("参数错误!key=" + key);
						}
						newKey = key.substring(0, index);
						isApply = key.substring(index).startsWith(Constant.APPLY_LINK);
					}
					// 多个字段的or查询
					if (newKey.contains(Constant.CLN_OR_LINK)) {
						SType finalSType = sType;
						List<String> newKeys = StrUtil.split(newKey, Constant.CLN_OR_LINK);
						orParams.add(newKeys.stream().map(v -> new SValue(finalSType, new SField<>(clazz, v), value))
								.collect(Collectors.toList()));
					} else {
						// apply 的情况
						if (isApply) {
							queryList.add(new SValue(sType, new SField<>(clazz, newKey),
									new SField<>(clazz, String.valueOf(value))));
						} else {
							if (value instanceof ArrayList && sType != SType.RANGE && sType != SType.NOT_RANGE
									&& sType != SType.RANGE_NE && sType != SType.IN && sType != SType.S_IN) {
								@SuppressWarnings("unchecked")
								ArrayList<Object> values = (ArrayList<Object>) value;
								for (Object sValue : values) {
									queryList.add(new SValue(sType, new SField<>(clazz, newKey), sValue));
								}
							} else {
								queryList.add(new SValue(sType, new SField<>(clazz, newKey), value));
							}
						}
					}
				}
			}
		}
		for (String s : removes) {
			params.remove(s);
		}
		if (clazz != null) {
			if (!StrUtil.isEmpty(this.showFlags)) {
				this.cascadeMap = getCascadeMap(clazz, this.showFlags);
				if (this.cascadeMap != null) {
					this.showFlagList = new ArrayList<>(this.cascadeMap.keySet());
				}
			}
			if (!StrUtil.isEmpty(tempFields)) {
				List<String> fields = StrUtil.split(tempFields, StrUtil.COMMA, -1, true, true);
				for (String showField : fields) {
					this.showFields.add(new SField<>(clazz, showField));
				}
			}
			sQuery = pQuery == null ? new SQuery<>(clazz) : pQuery;
			// 排序规则
			List<OrderBy> list = getOrderByList(clazz, this.orderBy);
			if (list == null) {
				sQuery.orderNo(null);
			} else {
				for (OrderBy by : list) {
					if (by.asc) {
						sQuery.orderAsc(by.sField);
					} else {
						sQuery.orderDesc(by.sField);
					}
				}
				// 如果没有ID排序,默认带自后order by id desc
				if (CollUtil.findOne(list, v -> v.getSField().getName().equals(Constant.ID_NAME)) == null) {
					if (this.isIdAsc) {
						sQuery.orderAsc(SQuery::idFun);
					} else {
						sQuery.orderDesc(SQuery::idFun);
					}
				}
			}

			sQuery.where(queryList);
			sQuery.andOr(andOrList);
			sQuery.show(this.showFields);
			sQuery.cascade(this.cascadeMap);
			sQuery.showText(this.showText);
			for (List<SValue> orParam : orParams) {
				sQuery.andOr(orParam);
			}
			if (this.isShowDefault()) {
				sQuery.showDefault();
			}
		}
	}

	// vue 容许提交查询类型
	public static SType getQueryType(String qryStr) {
		switch (qryStr) {
			case "$=":
			case "$Eq":
			case "$Ap=":
			case "$ApEq":
				return SType.EQ;
			case "$>":
			case "$Gt":
			case "$Ap>":
			case "$ApGt":
				return SType.GT;
			case "$<":
			case "$Lt":
			case "$Ap<":
			case "$ApLt":
				return SType.LT;
			case "$>=":
			case "$Ge":
			case "$Ap>=":
			case "$ApGe":
				return SType.GE;
			case "$<=":
			case "$Le":
			case "$Ap<=":
			case "$ApLe":
				return SType.LE;
			case "$!=":
			case "$Ne":
			case "$Ap!=":
			case "$ApNe":
				return SType.NE;
			case "$In":
				return SType.IN;
			case "$NotIn":
				return SType.NOT_IN;
			case "$Lk":
			case "$Like":
				return SType.LIKE;
			case "$LkRt":
				return SType.LIKE_RIGHT;
			case "$LkLt":
				return SType.LIKE_LEFT;
			case "$IsNull":
				return SType.IS_NULL;
			case "$NotNull":
				return SType.NOT_NULL;
			case "$IsEmpty":
				return SType.IS_EMPTY;
			case "$NotEmpty":
				return SType.NOT_EMPTY;
			case "$Range":
				return SType.RANGE;
			case "$RangeNe":
				return SType.RANGE_NE;
			case "$NotRange":
				return SType.NOT_RANGE;
			case "$SIn":
				return SType.S_IN;
		}
		return null;
	}

	public static List<String> getVueLinks() {
		return CollUtil.toList("$=", "$Eq", "$Ap=", "$ApEq", "$>", "$Gt", "$Ap>", "$ApGt", "$<", "$Lt", "$Ap<",
				"$ApLt", "$>=", "$Ge", "$Ap>=", "$ApGe", "$<=", "$Le", "$!=", "$Ne", "$Ap!=", "$ApNe", "$In", "$NotIn",
				"$Lk", "$Like", "$LkRt", "$LkLt", "$IsNull", "$NotNull", "$IsEmpty", "$NotEmpty", "$SIn", "$Range");
	}

	// 是否正序排序
	private List<OrderBy> getOrderByList(Class<SE> clazz, String orderByStr) {
		if (clazz == null || StrUtil.isEmpty(orderByStr) || "NULL".equalsIgnoreCase(orderByStr)) {
			return null;
		}
		List<OrderBy> list = new ArrayList<>();
		List<String> orders = StrUtil.split(orderByStr, StrUtil.COMMA);
		for (String order : orders) {
			String field = order.substring(0, order.indexOf(QUERY_LINK));
			if (field.equals(Constant.ID_NAME)) {
				// 兼容 PriKey != id 表结构设计
				field = FieldUtil.getPriKeyName(clazz);
			}
			if (order.endsWith("_asc") || order.endsWith("$asc") || order.endsWith("$Asc")) {
				list.add(new OrderBy(true, new SField<>(clazz, field)));
			} else if (order.endsWith("_desc") || order.endsWith("$desc") || order.endsWith("$Desc")) {
				list.add(new OrderBy(false, new SField<>(clazz, field)));
			}
		}
		return list;
	}

	// showFlags获取有意义到值
	private Map<SField<SE>, List<SField<?>>> getCascadeMap(Class<SE> clazz, String showFlags) {
		if (StrUtil.isEmpty(showFlags)) {
			return null;
		}
		showFlags = showFlags.replace(", ", ",");
		char[] chars = showFlags.toCharArray();
		int start = 0;
		for (int i = 0; i < chars.length; i++) {
			char c = chars[i];
			if (c == '[') {
				start = i;
			}
			if (start > 0) {
				if (c == ',') {
					chars[i] = '.';
				}
			}
			if (c == ']') {
				start = 0;
			}
		}
		showFlags = String.valueOf(chars);
		Map<SField<SE>, List<SField<?>>> backMap = new HashMap<>();
		List<String> list = StrUtil.split(showFlags, StrUtil.COMMA, -1, true, true);
		for (String s : list) {
			int index = s.indexOf('[');
			String key = s, value = "";
			if (index > -1) {
				key = s.substring(0, index);
				value = s.substring(index + 1, s.length() - (s.endsWith("]") ? 1 : 0));
			}
			showFlag.add(key);
			// 带点和不带点[.]都进入fillBean
			if (!key.contains(".") && !key.startsWith(QUERY_LINK)) {
				List<SField<?>> sFieldList = new ArrayList<>();
				if (!ReflectUtil.hasField(clazz, key)) {
					throw new RuntimeException("类{"+clazz+"}不存在属性:{"+key+"}");
				}
				Field field = ReflectUtil.getField(clazz, key);
				if (FieldUtil.getCascade(field) != null) {
					List<String> linkFields = StrUtil.split(value, StrUtil.DOT, -1, true, true);
					boolean isList = List.class == field.getType();
					Class<?> linkClass = isList ? FieldUtil.getFieldClass(field) : field.getType();
					for (String linkField : linkFields) {
						sFieldList.add(new SField<>(linkClass, linkField));
					}
					backMap.put(new SField<>(field), sFieldList);
				}
			}
		}
		return backMap;
	}

	@Data
	@AllArgsConstructor
	class OrderBy {
		boolean asc;
		SField<SE> sField;
	}
}